1. Field of the Invention
The invention relates generally to code optimization, and, more specifically, relates to optimization of code that implements constant loading.
2. Description of the Related Art
As the speed of processors continues to grow at a much higher rate than that of memory systems, it is becoming increasingly important to optimize applications for memory related operations.
Contemporary compilers use a wide range of optimization techniques to maximize the use of machine registers and avoid costly memory references whenever possible. Particularly, compilers try to improve the efficiency of the generated code with respect to usage of constants, leveraging mechanisms provided by many programming languages to create data objects with constant values. Compilers can use this knowledge to propagate constants in the code and to avoid unnecessary memory references. However, the optimization scope is often limited to just one module at a time. A compiler, not having the complete view of a program, is often forced to generate inefficient code. For example, when a module is accessing an externally defined constant, the compiler has to generate code that will calculate the address of the constant and load its value. The reason for loading the constant at run-time and not using its value directly is that this value is simply not visible at compile time. Even if compilers had access to other modules, many constants, such as addresses, would not be available because their values would not be finalized until after the link phase.
These limitations may result in many redundant address calculations and memory references, both weighing on memory hierarchy, adversely affecting the performance of the application. Address calculation and load instructions will increase pressure on the instruction cache, and access to data will disrupt the data cache. Consequently, address calculation and load instructions may cause misses on higher cache levels, misses in iTLB and dTLB, page faults, accesses to disk, etc., thus slowing the application.
Often, despite severe performance drawbacks, application developers choose to use externally-defined constants because of quite useful side effects. For example, the usual technique for incorporating changes into a large application is rebuilding only the modules that are affected by the changes and then relinking these rebuilt modules with the untouched modules to get an updated version of the large application. If the scope of the changes is global (e.g., change of global structures) incorporation of changes to the large application may require complete recompilation. If structure changes frequently, this technique for incorporating changes into a large application may prove to be slow and inefficient. This inefficiency propagates to the development process and significantly slows the development process as well. In these situations, developers often may choose to prepare the application for handling certain data structures at runtime. For example, an offset table that would hold an offset for each field in this structure may be defined for each global structure. Throughout the application, each time a code needs to access a field in a structure of this type, it would have to first load the field's offset from the table at run time. This approach allows the old code to handle the updated structures while avoiding recompilation. Hence, if some structure is changed, a developer updates the offset table for that structure, perhaps recompiles a few affected files, and re-links. The flexibility of this approach, however, comes at a high cost to performance. Each field access incurs a redundant load (offset) with associated redundant instructions.